Türkçe

TypeScript soyut sınıflarını, faydalarını ve karmaşık projelerde kodun yeniden kullanılabilirliğini ve esnekliğini artıran kısmi uygulama desenlerini keşfedin. Pratik örnekler ve en iyi uygulamaları içerir.

TypeScript Soyut Sınıflar: Kısmi Uygulama Desenlerinde Uzmanlaşmak

Soyut sınıflar, nesne yönelimli programlamanın (OOP) temel bir kavramıdır ve diğer sınıflar için bir şablon görevi görür. TypeScript'te soyut sınıflar, türetilmiş sınıflara belirli uygulama gereksinimlerini zorunlu kılarken ortak işlevselliği tanımlamak için güçlü bir mekanizma sunar. Bu makale, TypeScript soyut sınıflarının inceliklerini, kısmi uygulama için pratik desenleri ve projelerinizde kodun yeniden kullanılabilirliğini, sürdürülebilirliğini ve esnekliğini nasıl önemli ölçüde artırabileceklerini ele almaktadır.

Soyut Sınıflar Nedir?

TypeScript'teki bir soyut sınıf, doğrudan bir örneği oluşturulamayan bir sınıftır. Diğer sınıflar için bir temel sınıf olarak hizmet eder ve türetilmiş sınıfların uygulaması (veya geçersiz kılması) gereken bir dizi özellik ve metot tanımlar. Soyut sınıflar abstract anahtar kelimesi kullanılarak bildirilir.

Temel Özellikler:

Neden Soyut Sınıflar Kullanılır?

Soyut sınıflar, yazılım geliştirmede çeşitli avantajlar sunar:

Temel Soyut Sınıf Örneği

TypeScript'te bir soyut sınıfın temel sözdizimini göstermek için basit bir örnekle başlayalım:


abstract class Animal {
 abstract makeSound(): string;

 move(): void {
 console.log("Moving...");
 }
}

class Dog extends Animal {
 makeSound(): string {
 return "Woof!";
 }
}

class Cat extends Animal {
 makeSound(): string {
 return "Meow!";
 }
}

//const animal = new Animal(); // Hata: Soyut bir sınıfın örneği oluşturulamaz.

const dog = new Dog();
console.log(dog.makeSound()); // Çıktı: Woof!
dog.move(); // Çıktı: Moving...

const cat = new Cat();
console.log(cat.makeSound()); // Çıktı: Meow!
cat.move(); // Çıktı: Moving...

Bu örnekte, Animal soyut bir metot olan makeSound() ve somut bir metot olan move()'a sahip soyut bir sınıftır. Dog ve Cat sınıfları Animal sınıfını genişletir ve makeSound() metodu için somut uygulamalar sağlar. `Animal` sınıfını doğrudan örneklemeye çalışmanın bir hatayla sonuçlandığını unutmayın.

Kısmi Uygulama Desenleri

Soyut sınıfların güçlü yönlerinden biri, kısmi uygulamalar tanımlama yeteneğidir. Bu, bazı metotlar için varsayılan bir uygulama sağlarken türetilmiş sınıfların diğerlerini uygulamasını zorunlu kılmanıza olanak tanır. Bu, kodun yeniden kullanılabilirliğini esneklikle dengeler.

1. Türetilmiş Sınıflar Tarafından Uygulanan Soyut Metotlar

Bu desende, soyut sınıf, türetilmiş sınıflar tarafından uygulanması *gereken* soyut bir metot bildirir, ancak temel bir uygulama sunmaz. Bu, türetilmiş sınıfları kendi mantıklarını sağlamaya zorlar.


abstract class DataProcessor {
 abstract fetchData(): Promise;
 abstract processData(data: any): any;
 abstract saveData(processedData: any): Promise;

 async run(): Promise {
 const data = await this.fetchData();
 const processedData = this.processData(data);
 await this.saveData(processedData);
 }
}

class APIProcessor extends DataProcessor {
 async fetchData(): Promise {
 // Bir API'den veri almak için uygulama
 console.log("Fetching data from API...");
 return { data: "API Data" }; // Örnek veri
 }

 processData(data: any): any {
 // API verisine özgü verileri işlemek için uygulama
 console.log("Processing API data...");
 return { processed: data.data + " - Processed" }; // Örnek işlenmiş veri
 }

 async saveData(processedData: any): Promise {
 // İşlenmiş veriyi API aracılığıyla bir veritabanına kaydetmek için uygulama
 console.log("Saving processed API data...");
 console.log(processedData);
 }
}

const apiProcessor = new APIProcessor();
apiProcessor.run();

Bu örnekte, DataProcessor soyut sınıfı üç soyut metot tanımlar: fetchData(), processData() ve saveData(). APIProcessor sınıfı DataProcessor sınıfını genişletir ve bu metotların her biri için somut uygulamalar sağlar. Soyut sınıfta tanımlanan run() metodu, tüm süreci yönetir ve her adımın doğru sırada yürütülmesini sağlar.

2. Soyut Bağımlılıklara Sahip Somut Metotlar

Bu desen, belirli görevleri gerçekleştirmek için soyut metotlara dayanan somut metotları soyut sınıfta içerir. Bu, uygulama ayrıntılarını türetilmiş sınıflara devrederken ortak bir algoritma tanımlamanıza olanak tanır.


abstract class PaymentProcessor {
 abstract validatePaymentDetails(paymentDetails: any): boolean;
 abstract chargePayment(paymentDetails: any): Promise;
 abstract sendConfirmationEmail(paymentDetails: any): Promise;

 async processPayment(paymentDetails: any): Promise {
 if (!this.validatePaymentDetails(paymentDetails)) {
 console.error("Invalid payment details.");
 return false;
 }

 const chargeSuccessful = await this.chargePayment(paymentDetails);
 if (!chargeSuccessful) {
 console.error("Payment failed.");
 return false;
 }

 await this.sendConfirmationEmail(paymentDetails);
 console.log("Payment processed successfully.");
 return true;
 }
}

class CreditCardPaymentProcessor extends PaymentProcessor {
 validatePaymentDetails(paymentDetails: any): boolean {
 // Kredi kartı detaylarını doğrula
 console.log("Validating credit card details...");
 return true; // Örnek doğrulama
 }

 async chargePayment(paymentDetails: any): Promise {
 // Kredi kartından ödeme al
 console.log("Charging credit card...");
 return true; // Örnek ödeme
 }

 async sendConfirmationEmail(paymentDetails: any): Promise {
 // Kredi kartı ödemesi için onay e-postası gönder
 console.log("Sending confirmation email for credit card payment...");
 }
}

const creditCardProcessor = new CreditCardPaymentProcessor();
creditCardProcessor.processPayment({ cardNumber: "1234-5678-9012-3456", expiryDate: "12/24", cvv: "123", amount: 100 });

Bu örnekte, PaymentProcessor soyut sınıfı, genel ödeme işleme mantığını ele alan bir processPayment() metodu tanımlar. Ancak, validatePaymentDetails(), chargePayment() ve sendConfirmationEmail() metotları soyuttur ve türetilmiş sınıfların her ödeme yöntemi (örneğin, kredi kartı, PayPal, vb.) için özel uygulamalar sağlamasını gerektirir.

3. Şablon Metot Deseni

Şablon Metot deseni, bir algoritmanın iskeletini soyut sınıfta tanımlayan ancak alt sınıfların yapısını değiştirmeden algoritmanın belirli adımlarını geçersiz kılmasına izin veren bir davranışsal tasarım desenidir. Bu desen, belirli bir sırada gerçekleştirilmesi gereken bir dizi işleminiz olduğunda, ancak bazı işlemlerin uygulanması bağlama göre değişebileceğinde özellikle kullanışlıdır.


abstract class ReportGenerator {
 abstract generateHeader(): string;
 abstract generateBody(): string;
 abstract generateFooter(): string;

 generateReport(): string {
 const header = this.generateHeader();
 const body = this.generateBody();
 const footer = this.generateFooter();

 return `${header}\n${body}\n${footer}`;
 }
}

class PDFReportGenerator extends ReportGenerator {
 generateHeader(): string {
 return "PDF Report Header";
 }

 generateBody(): string {
 return "PDF Report Body";
 }

 generateFooter(): string {
 return "PDF Report Footer";
 }
}

class CSVReportGenerator extends ReportGenerator {
 generateHeader(): string {
 return "CSV Report Header";
 }

 generateBody(): string {
 return "CSV Report Body";
 }

 generateFooter(): string {
 return "CSV Report Footer";
 }
}

const pdfReportGenerator = new PDFReportGenerator();
console.log(pdfReportGenerator.generateReport());

const csvReportGenerator = new CSVReportGenerator();
console.log(csvReportGenerator.generateReport());

Burada, `ReportGenerator` genel rapor oluşturma sürecini `generateReport()` içinde tanımlarken, bireysel adımlar (başlık, gövde, alt bilgi) somut alt sınıflar olan `PDFReportGenerator` ve `CSVReportGenerator`'a bırakılmıştır.

4. Soyut Özellikler

Soyut sınıflar, türetilmiş sınıflarda uygulanması gereken özellikler olan soyut özellikleri de tanımlayabilir. Bu, türetilmiş sınıflarda belirli veri öğelerinin varlığını zorunlu kılmak için kullanışlıdır.


abstract class Configuration {
 abstract apiKey: string;
 abstract apiUrl: string;

 getFullApiUrl(): string {
 return `${this.apiUrl}/${this.apiKey}`;
 }
}

class ProductionConfiguration extends Configuration {
 apiKey: string = "prod_api_key";
 apiUrl: string = "https://api.example.com/prod";
}

class DevelopmentConfiguration extends Configuration {
 apiKey: string = "dev_api_key";
 apiUrl: string = "http://localhost:3000/dev";
}

const prodConfig = new ProductionConfiguration();
console.log(prodConfig.getFullApiUrl()); // Çıktı: https://api.example.com/prod/prod_api_key

const devConfig = new DevelopmentConfiguration();
console.log(devConfig.getFullApiUrl()); // Çıktı: http://localhost:3000/dev/dev_api_key

Bu örnekte, Configuration soyut sınıfı iki soyut özellik tanımlar: apiKey ve apiUrl. ProductionConfiguration ve DevelopmentConfiguration sınıfları Configuration sınıfını genişletir ve bu özellikler için somut değerler sağlar.

İleri Düzey Konular

Soyut Sınıflarla Mixin'ler

TypeScript, daha karmaşık ve yeniden kullanılabilir bileşenler oluşturmak için soyut sınıfları mixin'lerle birleştirmenize olanak tanır. Mixin'ler, daha küçük, yeniden kullanılabilir işlevsellik parçalarını birleştirerek sınıflar oluşturmanın bir yoludur.


// Bir sınıfın kurucusu için bir tür tanımla
type Constructor = new (...args: any[]) => T;

// Bir mixin işlevi tanımla
function Timestamped(Base: TBase) {
 return class extends Base {
 timestamp = new Date();
 };
}

// Başka bir mixin işlevi
function Logged(Base: TBase) {
 return class extends Base {
 log(message: string) {
 console.log(`${this.constructor.name}: ${message}`);
 }
 };
}

abstract class BaseEntity {
 abstract id: number;
}

// Mixin'leri BaseEntity soyut sınıfına uygula
const TimestampedEntity = Timestamped(BaseEntity);
const LoggedEntity = Logged(TimestampedEntity);

class User extends LoggedEntity {
 id: number = 123;
 name: string = "John Doe";

 constructor() {
 super();
 this.log("User created");
 }
}

const user = new User();
console.log(user.id); // Çıktı: 123
console.log(user.timestamp); // Çıktı: Mevcut zaman damgası
user.log("User updated"); // Çıktı: User: User updated

Bu örnek, üçünün de işlevselliğini devralan bir User sınıfı oluşturmak için Timestamped ve Logged mixin'lerini BaseEntity soyut sınıfıyla birleştirir.

Bağımlılık Enjeksiyonu

Soyut sınıflar, bileşenleri ayırmak ve test edilebilirliği artırmak için bağımlılık enjeksiyonu (DI) ile etkili bir şekilde kullanılabilir. Bağımlılıklarınız için arayüz olarak soyut sınıfları tanımlayabilir ve ardından somut uygulamaları sınıflarınıza enjekte edebilirsiniz.


abstract class Logger {
 abstract log(message: string): void;
}

class ConsoleLogger extends Logger {
 log(message: string): void {
 console.log(`[Console]: ${message}`);
 }
}

class FileLogger extends Logger {
 log(message: string): void {
 // Bir dosyaya loglama uygulaması
 console.log(`[File]: ${message}`);
 }
}

class AppService {
 private logger: Logger;

 constructor(logger: Logger) {
 this.logger = logger;
 }

 doSomething() {
 this.logger.log("Doing something...");
 }
}

// ConsoleLogger'ı enjekte et
const consoleLogger = new ConsoleLogger();
const appService1 = new AppService(consoleLogger);
appService1.doSomething();

// FileLogger'ı enjekte et
const fileLogger = new FileLogger();
const appService2 = new AppService(fileLogger);
appService2.doSomething();

Bu örnekte, AppService sınıfı Logger soyut sınıfına bağlıdır. Somut uygulamalar (ConsoleLogger, FileLogger) çalışma zamanında enjekte edilir, bu da farklı loglama stratejileri arasında kolayca geçiş yapmanızı sağlar.

En İyi Uygulamalar

Sonuç

TypeScript soyut sınıfları, sağlam ve sürdürülebilir uygulamalar oluşturmak için güçlü bir araçtır. Kısmi uygulama desenlerini anlayarak ve uygulayarak, esnek, yeniden kullanılabilir ve iyi yapılandırılmış kod oluşturmak için soyut sınıfların avantajlarından yararlanabilirsiniz. Varsayılan uygulamalara sahip soyut metotları tanımlamaktan, mixin'ler ve bağımlılık enjeksiyonu ile soyut sınıfları kullanmaya kadar olasılıklar çok geniştir. En iyi uygulamaları takip ederek ve tasarım seçimlerinizi dikkatlice düşünerek, TypeScript projelerinizin kalitesini ve ölçeklenebilirliğini artırmak için soyut sınıfları etkili bir şekilde kullanabilirsiniz.

İster büyük ölçekli bir kurumsal uygulama ister küçük bir yardımcı kütüphane oluşturuyor olun, TypeScript'te soyut sınıflarda uzmanlaşmak şüphesiz yazılım geliştirme becerilerinizi geliştirecek ve daha sofistike ve sürdürülebilir çözümler oluşturmanızı sağlayacaktır.